//  NSGAII.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
package jmetal.metaheuristics.nsgaII;

import jmetal.core.*;
import jmetal.metaheuristics.moeansm.util.TempoExecucao;
import jmetal.metaheuristics.moeansm.util.UtilAP;
import jmetal.qualityIndicator.QualityIndicator;
import jmetal.util.Distance;
import jmetal.util.JMException;
import jmetal.util.Ranking;
import jmetal.util.comparators.CrowdingComparator;

/**
 * Implementation of NSGA-II. This implementation of NSGA-II makes use of a
 * QualityIndicator object to obtained the convergence speed of the algorithm.
 * This version is used in the paper: A.J. Nebro, J.J. Durillo, C.A. Coello
 * Coello, F. Luna, E. Alba "A Study of Convergence Speed in Multi-Objective
 * Metaheuristics." To be presented in: PPSN'08. Dortmund. September 2008.
 */
public class NSGAIIBuscaLocal extends Algorithm {

    /**
     * Constructor
     *
     * @param problem Problem to solve
     */
    public NSGAIIBuscaLocal(Problem problem) {
        super(problem);
    } // NSGAII

    /**
     * Runs the NSGA-II algorithm.
     *
     * @return a <code>SolutionSet</code> that is a set of non dominated
     * solutions as a result of the algorithm execution
     * @throws JMException
     */
    public SolutionSet execute() throws JMException, ClassNotFoundException {
        int populationSize;
        int maxEvaluations;
        int evaluations;

        TempoExecucao tempoExecucao = new TempoExecucao();

        QualityIndicator indicators; // QualityIndicator object
        int requiredEvaluations; // Use in the example of use of the
        // indicators object (see below)
         /*ESTAGNACAO*/
        int estagnacao = 0;
        int MAX_ESTAGNACAO = 200;
        double HV = 0.0;

        SolutionSet population;
        SolutionSet offspringPopulation;
        SolutionSet union;

        Operator mutationOperator;
        Operator crossoverOperator;
        Operator selectionOperator;
        Operator buscaLocalOperator;

        Distance distance = new Distance();
        //Read the parameters
        populationSize = ((Integer) getInputParameter("populationSize"));
        maxEvaluations = ((Integer) getInputParameter("maxEvaluations"));
        indicators = (QualityIndicator) getInputParameter("indicators");

        //Initialize the variables
        population = new SolutionSet(populationSize);
        evaluations = 0;

        requiredEvaluations = 0;

        //Read the operators
        mutationOperator = operators_.get("mutation");
        crossoverOperator = operators_.get("crossover");
        selectionOperator = operators_.get("selection");
        buscaLocalOperator = operators_.get("buscaLocal");

        // Create the initial solutionSet
        Solution newSolution;
        for (int i = 0; i < populationSize; i++) {
            newSolution = new Solution(problem_);

            int objetivoSelecionado = UtilAP.obterNumeroAleatorio(0, 2);
            newSolution.setObjetivoBuscaLocal(objetivoSelecionado);
            newSolution.setInstancia((objetivoSelecionado == 0) ? problem_.instancia1 : problem_.instancia2);
            newSolution = (Solution) buscaLocalOperator.execute(newSolution);

            problem_.evaluate(newSolution);
            problem_.evaluateConstraints(newSolution);
            evaluations++;
            population.add(newSolution);
        } //for       

        // Generations 
        while (evaluations < maxEvaluations) {

            // Create the offSpring solutionSet      
            offspringPopulation = new SolutionSet(populationSize);
            Solution[] parents = new Solution[2];
            for (int i = 0; i < (populationSize / 2); i++) {
                if (evaluations < maxEvaluations) {
                    //obtain parents
                    parents[0] = (Solution) selectionOperator.execute(population);
                    parents[1] = (Solution) selectionOperator.execute(population);
                    Solution[] offSpring = (Solution[]) crossoverOperator.execute(parents);
//                    mutationOperator.execute(offSpring[0]);
//                    mutationOperator.execute(offSpring[1]);

                    int objetivoSelecionado = UtilAP.obterNumeroAleatorio(0, 2);
                    offSpring[0].setObjetivoBuscaLocal(objetivoSelecionado);
                    offSpring[0].setInstancia((objetivoSelecionado == 0) ? problem_.instancia1 : problem_.instancia2);
                    offSpring[0] = (Solution) buscaLocalOperator.execute(offSpring[0]);

                    objetivoSelecionado = UtilAP.obterNumeroAleatorio(0, 2);
                    offSpring[1].setObjetivoBuscaLocal(objetivoSelecionado);
                    offSpring[1].setInstancia((objetivoSelecionado == 0) ? problem_.instancia1 : problem_.instancia2);
                    offSpring[1] = (Solution) buscaLocalOperator.execute(offSpring[1]);

                    problem_.evaluate(offSpring[0]);
                    problem_.evaluateConstraints(offSpring[0]);
                    problem_.evaluate(offSpring[1]);
                    problem_.evaluateConstraints(offSpring[1]);
                    offspringPopulation.add(offSpring[0]);
                    offspringPopulation.add(offSpring[1]);
                    evaluations += 2;
                } // if                            
            } // for

            // Create the solutionSet union of solutionSet and offSpring
            union = ((SolutionSet) population).union(offspringPopulation);

            // Ranking the union
            Ranking ranking = new Ranking(union);

            int remain = populationSize;
            int index = 0;
            SolutionSet front = null;
            population.clear();

            // Obtain the next front
            front = ranking.getSubfront(index);

            while ((remain > 0) && (remain >= front.size())) {
                //Assign crowding distance to individuals
//        distance.crowdingDistanceAssignment(front, problem_.getNumberOfObjectives());
                //Add the individuals of this front
                for (int k = 0; k < front.size(); k++) {
                    population.add(front.get(k));
                } // for

                //Decrement remain
                remain = remain - front.size();

                //Obtain the next front
                index++;
                if (remain > 0) {
                    front = ranking.getSubfront(index);
                } // if        
            } // while

            // Remain is less than front(index).size, insert only the best one
            if (remain > 0) {  // front contains individuals to insert                        
                distance.crowdingDistanceAssignment(front, problem_.getNumberOfObjectives());
                front.sort(new CrowdingComparator());
                for (int k = 0; k < remain; k++) {
                    population.add(front.get(k));
                } // for

                remain = 0;
            } // if                               

            // This piece of code shows how to use the indicator object into the code
            // of NSGA-II. In particular, it finds the number of evaluations required
            // by the algorithm to obtain a Pareto front with a hypervolume higher
            // than the hypervolume of the true Pareto front.
            if ((indicators != null) && (requiredEvaluations == 0)) {
                double HVAtual = indicators.getHypervolume(population);
                if (HV >= HVAtual && HV != 0.0) {
                    estagnacao++;
                } else {
                    HV = HVAtual;
                    estagnacao = 0;
                }
                if (estagnacao >= MAX_ESTAGNACAO) {
                    System.out.println("NSGAII BL Estagnou na geracao: " + evaluations);
                    break;
                }
            } // if

        } // while

        // Return as output parameter the required evaluations
        setOutputParameter("requiredEvaluations", requiredEvaluations);

        // Return the first non-dominated front
        Ranking ranking = new Ranking(population);
//        ranking.getSubfront(0).printFeasibleFUN("FUN_NSGAII");
        ranking.getSubfront(0).setTempoExecucao(tempoExecucao.getTempo());
        ranking.getSubfront(0).setGeracaoParou(evaluations);
        return ranking.getSubfront(0);
    } // execute
} // NSGA-II
